# 第四章 从第一个简单的Welcome页面开始小程序之旅
本章我们将正式开始Orange Can项目的编码工作。项目从编写一个最简单的“welcome”欢迎页面开始,并在编写页面的过程中逐步介绍小程序的基本文件结构、CSS的使用限制、自适应单位rpx、全局样式、App.json配置文件以及Flex布局等小程序开发的必备知识。
# 4.1 认识小程序的基本文件结构
我们还是以第3章中新建的官方示例项目HelloWorld为参考,来看一下构成一个小程序的基本文件要素,如图==下图==所示。
图:官方示例项目的文件及文件结构
不同于其他框架,小程序的目录结构非常简单,也非常易于理解。
首先我们看到根目录下面有3个文件:app.js、app.json和app.wxss。一个小程序项目必须有这3个描述App的文件,它们必须放在应用程序的根目录下,否则小程序会提示找不到app.json文件。表3-1描述了3个文件的意义。
==表== app.js、app.json和app.wxss的含义
文件 | 必填 | 作用 |
---|---|---|
app.js | 是 | 小程序逻辑文件 |
app.json | 是 | 小程序配置文件 |
app.wxss | 否 | 全局公共样式文件 |
这3个文件是应用程序级别的文件。
接着是和这3个应用程序级别文件平行的pages文件夹,一个小程序通常由若干个页面构成,pages文件夹通常用于存放小程序的页面文件。比如==图==中pages文件夹下就有2个页面,分别是index页面和logs页面。每个页面可以由4个文件构成,分别是:.js、.wxml、.wxss和.json文件。
==表==描述了这4个页面文件的意义。
文件类型 | 必填 | 作用 |
---|---|---|
js | 是 | 页面逻辑 |
wxml | 是 | 页面结构 |
wxss | 否 | 页面样式表 |
json | 否 | 页面配置” |
其实,这4个文件的作用大家并不陌生。我们可以和熟悉的Web前端开发技术做一个对比:
wxml文件类似于我们熟悉的HTML文件,用来编写页面的标签和骨架,不同的是wxml文件里的标签元素不可以使用HTML标签,只能使用小程序自己封装的一些组件,这些组件也是我们后面要重点学习的知识。
wxss文件的作用类似于我们熟悉的CSS文件,用于编写小程序的样式,实际上小程序的样式编写语言就是CSS,只是把.css文件换成了.wxss文件。
json文件用来配置页面的样式与行为。
js文件类似于我们前端编程中的JavaScript文件,用来编写小程序的页面逻辑。
以上4种类型的页面文件的文件名称必须相同,这是要注意的一个地方(如果不同,小程序是无法将这4个文件视作是同一个页面的文件)。
我们可以看到,小程序的4种页面级别文件同3个应用程序级别文件相比,多出了一个wxml页面标签文件,对于其他3种类型的文件(json、js、wxss),页面级别和应用程序级别的作用基本相似,只不过页面文件作用于页面本身而应用程序文件作用于应用程序整体。
除了pages文件夹外,官方的示例项目中还有一个utils文件夹,这个文件夹用来存放一些公共的js文件,比如utils下面的util.js。我们可以任意定义类似于utils文件夹的目录,并放在小程序的任意位置,小程序对此并没有任何限制。
关于小程序目录命名规则 官方示例项目中有一个pages文件夹,很多开发者会认为这个命名是不能更改的。其实小程序对于目录和文件的命名并没有任何强制的约束,小程序并不是一个标准的“约定大于配置”类型的框架(开发者可自行搜索什么是“约定大于配置”)。实际上,pages、utils以及index和logs目录的名字都是可以随意更改的。甚至logs文件夹下的4个文件名字也不必一定是log.xxxx,你可以更改为log1,log2都是可以的,但这4个页面文件的文件名必须保持一致。
# 4.2 开始动手编写第一个小程序页面
掌握以上的少量知识,我们就可以开始编写小程序了,是不是很惊奇。是的,小程序就是这样一门适合实践的技术,让我们马上开始吧。
我们从零开始新建一个项目。每个项目都有一个自己的名字,比如Google的 Tensor Flow(一个机器学习项目)、淘宝的Ocean Base(一个分布式数据库)、微软的Azure(云计算“蔚蓝”),还有大家写代码使用的各类框架和库:Flask、Spring、jQuery。我们的项目虽小,但还是要给它起个名字,就叫“Orange Can”吧。没有什么特殊的意思,纯粹是因为笔者现在想吃“桔子罐头”了。大家可以随意来给项目命名。
我们按照在第三章中所讲的方式,再新建一个项目,项目名称填入Orange-Can(记得填入AppID,可以使用之前HelloWorld所用的AppID)。当然,如果你愿意,也可以使用上个章节中我们所创建的“HelloWorld”项目。
因为我们要从零开始编写一个项目,所以我们想创建一个没有任何文件的空白小程序项目。但可惜的是,开发工具并没有提供创建“空白项目”的功能,我们只能创建官方的示例项目。现在,我们将Orange-Can项目下的所有文件全部删除,一个不留。在后面的开发过程中,我们将手动新建项目的每一个文件,这样的方式可以帮助开发者更好的理解每个小程序文件的意义。
删除所有文件后,重新编译项目,小程序将会出现==如图==所示的错误提示。
图:删除所有项目文件后,小程序会提示错误
这是因为现在的项目里没有任何文件,由于缺少必要的文件,所以小程序会报错。如果大家不想经历这些错误,那么可以在官方提供的示例项目上进行修改。
错误信息提示我们缺少app.json文件。我们首先把第三章中所提到的3个应用程序文件app.json、app.js和app.wxss文件新建在项目的根目录下(点击文件管理器上方的“+”号创建)。添加文件后的项目情况如==下图==所示。
图:新建3个应用程序文件后的小程序项目情况
这时候,小程序依然会提示错误信息。错误信息是非常重要的错误排查手段,开发者必须重视。但这里我们会发现,这个错误提示我们是看不懂的。
并不是任何的错误信息可读性都是非常强的。我们在开发时很多时候都会遇到一些看不懂的错误信息。虽然错误信息不一定能够非常直接的告诉我们到底错在哪里,但错误信息里的一些异常信息诸如:错误发生的文件、错误发生的代码行数等等,这些信息确是非常有用的。同时,上图的代码错误形式通常表示的是JSON类型文件中的代码有问题,如果开发者在以后的开发过程中遇到类似的错误形式,请第一时间排查JSON文件。这是一个经验性的总结,有经验的开发者确实能够很快的定位错误。
仔细看上图,错误信息里也确实提示了app.json是有问题的,只是没有告诉我们具体是什么错误。我们打开app.json文件,会发现app.json文件里是空白的。在小程序中app.json文件不允许是空白的文件,它至少需要一个基本的json结构体。我们在app.json文件中加入一对空的花括号 { }(花括号就是json的基本结构)。保存后我们会发现小程序依然提示有错误,但是错误信息变了。这说明我们已经修复了app.json的问题,但还有别的错误。没有关系,我们先忽略它。
小程序对于json文件的处理比较严格。比如,不能是空白的文件,至少需要一对花括号{ };json文件中不能用注释的代码,否则也会出现错误;json文件中不能使用单引号,字符串必须使用双引号(JSON格式规范中的引号规定必须是双引号)。 简单点来说,一个.json文件必须严格遵守json数据格式,否则就无法通过开发工具检测。我们特别提出这一点,希望开发者注意。
我们继续在项目根目录下新建一个pages文件夹,并在pages文件夹下新建一个名为welcome的文件夹(依然通过“+”号创建),接着再在welcome文件夹下新建4个页面文件:welcome.js、welcome.wxml、welcome.wxss和welcome.json。完成以上操作后,第一个页面“welcome”所需要的全部文件就新建完毕了。这时候开发工具可能依然提示==下图==所示的错误,继续忽略它。
图:app.json中的错误
在新建4个页面文件时,我们不需要逐一新建。右键点击welcome文件夹,选择【新建Page】选项。接着输入“welcome”,小程序会自动为我们一次性在welcome文件夹下生成4个页面文件。很方便。建议开发者统一使用这种方式创建页面文件,否则会出现各种各样的问题。
当你通过右键点击菜单的方式新建完成welcome页面后会发现,刚刚小程序中的错误信息消失了。这是怎么回事儿呢?此时我们打开app.json文件,你会发现一个有趣的现象:小程序自动的在app.json中添加了一段代码:
代码清单 app.json
{
"pages": [
"pages/welcome/welcome"
]
}
2
3
4
5
要理解这段代码,我们需要知道一个关于小程序页面的重要机制:所有小程序的页面都需要在app.json中注册,而注册的方式也非常简单,就是在app.json文件的pages数组项下加入页面的路径,比如上面的代码清单中就加入了 "pages/welcome/welcome",这段代码就是welcome页面的路径,它是由【路径+名称】所构成。"pages/welcome/"代表页面的路径,而第二个"welcome"代表页面名称。所有小程序页面路径都遵循这样的规则,当我们在后面的章节中遇到页面路径时,请开发者记得这种路径模式。
这里我们要强调的是,如果小程序的页面是通过【新建Page】的菜单生成的,小程序是会自动把页面的路径注册到app.json中的。但是如果不是通过这种工具的方式新建的,而是自己手动新建的页面文件,那么小程序是不会帮你自动注册的。这时需要开发者手动在app.json中加入以上注册代码。但通常来说,我们都会选择用【新建Page】的方式来创建页面,所以这里的路径注册其实是不需要我们手动添加的。
关于页面的路径我们还有几点需要说明:
- 页面路径前面不要加“/”。形如“/pages/welcome/welcome”这样的路径是错误的。如果加入了“/”,小程序会提示错误:无法找到welcome页面。
- 路径最后一段welcome,不需要指定具体的文件扩展名,无须写成pages/welcome/welcome.wxml。MINA框架将会自动按照页面路径的指示,将页面的.json、.js、.wxml和.wxss这4个文件进行整合。
如果项目中有多个页面,则需要将每个页面的路径加入到pages这个数组下。虽然我们现在只有一个welcome页面,但随着Orange Can项目的不断开发,我们将在pages下面加入越来越多的页面路径。 下面代码是Orange Can项目开发完成后的pages注册情况。
==代码清单== app.json
{
"pages":[
"pages/welcome/welcome",
"pages/posts/posts",
"pages/movies/more-movie/more-movie",
"pages/movies/movies",
"pages/movies/movie-detail/movie-detail",
"pages/posts/post-detail/post-detail",
"pages/posts/post-item/post-item",
"pages/posts/post-comment/post-comment"
]
}
2
3
4
5
6
7
8
9
10
11
12
还需要提醒开发者注意的是,不仅添加页面时需要在app.json中注册页面路径,当我们删除一个页面时也需要将pages下的页面路径删除掉,否则开发工具会提示找不到页面。【新建Page】会自动在app.json中添加页面路径,但开发工具没有提供【删除Page】的功能。所以当我们将页面文件删除后,请务必手动将app.json中对应的页面路径删除掉。
再次提醒开发者,请务必尽量使用开发工具所提供的创建功能来创建各种文件,比如使用【新建Page】、【新建Component】(关于Component自定义组件,我们在后续章节中会详细讲解)等功能来创建页面和自定义组件。小程序的一些特定文件必须有特定的初始代码,不允许为空文件,否则小程序就会报错。比如之前我们提到过json类型的文件至少要有一个空的{ };再比如,页面的js文件必须要有Page({ })作为初始代码(注意这里强调的是页面的js文件,普通js文件可以是空的)。
当我们按照以上的步骤新建完所有目录和文件后,文件管理器中的文件结构应该如==下图==所示(务必确保调试器中没有错误出现):
图:完成新建welcome页面后的项目文件结构
同时,左侧的小程序模拟器将显示出我们新建的welcome页面,如==下图==所示。它显示了一行文本"pages/welcome/welcome.wxml"。这段文本并不是我们自己编写的,而是开发工具在新建页面时自动为我们添加的一段文本,它位于welcome.wxml文件中。
图:完成新建welcome页面后的模拟器运行情况
本小节,我们详细介绍了小程序新建文件的一些基本操作以及常见错误。下个小节,我们将继续编写我们的welcome欢迎页面。
# 4.3 构建welcome页面的元素和样式
在上一小节中,我们仅仅正确地加载和显示了welcome页面,还没有编写任何页面代码。在本小节中,我们将尝试为welcome页面添加一些标签元素。
可以在本书的彩页中查看welcome页面的最终设计图。这个设计图的设计元素非常简单,仅由一张圆形的图片、一段文本和一个按钮构成。下面我们一起来完成第一个小程序页面吧。
首先打开welcome.wxml文件,删除开发工具自动创建的默认代码,并在该文件中键入以下代码,这段代码将welcome页面所需要的标签元素全部填入到页面源文件中。
==代码清单== welcome.wxml
<view>
<text>Hello , 桔子罐头</text>
<view>
<text>开启小程序之旅</text>
</view>
</view>
2
3
4
5
6
这段代码总共使用了3个微信小程序的组件,分别是view、text和image组件。view组件通常作为容器来使用,类似于HTML中的<div>
标签;text组件用来显示一段文本,类似于HTML中的·<span>
标签,本例中第一个text组件就被用来显示一段文本“Hello,桔子罐头”;而image组件用来显示一张图片,类似于HTML中的<img>
标签。
大家应该注意到了,笔者在描述这些元素时的用词区别。描述wxml元素使用的是“组件”,而描述HTML元素使用的是“标签”,这是符合规范称呼的。HTML是标记语言,它的标签主要是用来标记页面骨架,标签的属性也比较少。但组件不同,组件除了标记的作用,它的属性一般也是非常丰富的。小程序官方文档中也将view、text、image称为组件,而并没有称为标签。
同HTML中的img标签一样,image组件需要设置一个src属性,该属性指向一张图片的路径,用来显示该图片。我们在项目的根目录下新建一个名为images的目录,用来存放Orange Can的所有图片资源。在images目录下新建avatar目录,然后将一些适合做头像的图片拷贝到avatar目录中。
如何将图片放入到小程序的目录中?微信Web开发者工具无法通过Ctrl+C、Ctrl+V的功能复制粘贴图片,也没有提供导入图片的功能。我们需要在操作系统中打开项目的目录,并将图片拷贝到对应的avatar文件夹中,小程序会自动刷新目录,并在开发工具中显示这些图片。
以上操作完成后,我们的工程目录将变成==如下图==所示的结构。
图:加入图片后的目录结构
现在在image组件中加入属性src:
<image src="/images/avatar/avatar-1.png"></image>
当保存项目后,模拟器立刻会出现这张图片,但图片显示的高宽并不是图片本身的高宽。它被MINA框架设置成了宽度300px、高度225px,这也是小程序默认的图片高宽。如果我们不显示地指定图片高宽,所有图片都将保持这个默认值。
相对路径与绝对路径 在小程序中同样有相对路径和绝对路径的区别。上面我们在设置image组件的src属性时,使用的是绝对路径,它以“/”开头,“/”代表根目录。我们也可以使用相对路径来为image指定图片路径,比如,将代码中image组件的src属性改写为相对路径:
<image src="../../images/avatar/avatar-1.png"></image>
路径中的“..”表示向上一级。这里由于使用绝对路径更加简洁,所以我们使用绝对路径。
代码中间部分使用一个view组件包裹一个“开启小程序之旅”的text组件来实现按钮部分。由于还没有编写样式,所以暂时它还不能呈现为一个按钮的形状。目前整个页面看起来是这个样子的,==如图==所示。
图:还没有加入样式代码时welcome页面的样子
现在来编写welcome页面的样式。小程序编写样式的语言就是CSS,我们应该将CSS代码写在页面的wxss文件中。在编写welcome.wxss文件之前,首先在welcome.wxml文件中给每个需要样式的组件加入样式名称class name。
==代码清单== welcome.wxml
<view class="container">
<image class="avatar" src="/images/avatar/avatar-1.png"></image>
<text class="motto">Hello, 桔子罐头</text>
<view class="journey-container">
<text class="journey">开启小程序之旅</text>
</view>
</view>
2
3
4
5
6
7
就和我们在HTML中编写CSS名称一样,不是吗? 接着我们将下面这段CSS代码加入到welcome.wxss文件中。
==代码清单== 编写welcome页面的样式 welcome.wxss
.container{
display: flex;
flex-direction:column;
align-items: center;
}
.avatar{
width:200rpx;
height:200rpx;
margin-top:160rpx;
}
.motto{
margin-top:100rpx;
font-size:32rpx;
font-weight: bold;
color: #9F4311;
}
.journey-container{
margin-top: 200rpx;
border: 1px solid #EA5A3C;
width: 200rpx;
height: 80rpx;
border-radius: 5px;
text-align:center;
}
.journey{
font-size:22rpx;
font-weight: bold;
line-height:80rpx;
color: #EA5A3C;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
让我们保存一下,看看页面发生了什么变化,==如图==所示。
图:添加样式后的welcome页面
下面简单介绍一下这些CSS代码的作用。
- .container 是所有组件元素的容器样式。这里使用Flex布局的方式,来控制容器下子元素的排布规则。关于Flex将在下个小节里具体讲解
- .avatar 设置头像图片的大小和位置
- .motto 设置“Hello,桔子罐头”这段文本的样式
- .journey-container设置了“开启小程序之旅”的外边框,使它们看起来更像一个按钮border-radius让这个外边框变成一个“圆角”的矩形
- .journey则设置了圆角矩形内的文本样式
本书的主要目的是讲解小程序的核心知识,并不是一本CSS和JavaScript的基础语法书。限于书籍的篇幅,我们只对CSS样式中的核心内容作较为深入的讲解。下个小节,我们来学习小程序官方推荐的布局方式:Flex布局。
真实项目中,图片资源尽量不要存储在小程序的目录中,因为小程序的大小不能超过2MB,超过则无法真机运行和发布项目。应该将图片都存放在服务器上,让小程序通过网络来加载图片资源。如果确实小程序包的大小超过了2MB,需要使用一种称为”分包加载“的机制,详情可参考官方文档关于”分包加载“章节。但分包加载在小程序中使用的并不多,超过2MB的分包加载将给用户带来一些不好的体验,建议开发者将小程序包的大小控制在2MB以内。
# 4.4 小程序所支持的CSS选择器
小程序文档中明确指出,在小程序中CSS只支持==下表==所示6种CSS选择器:
选择器 | 样例 | 样例描述 |
---|---|---|
.class | .intro | 选择所有拥有 class="intro" 的组件 |
#id | #firstname | 选择拥有 id="firstname" 的组件 |
element | view | 选择所有 view 组件 |
element, element | view, checkbox | 选择所有文档的 view 组件和所有的 checkbox 组件 |
::after | view::after | 在 view 组件后边插入内容 |
::before | view::before | 在 view 组件前边插入内容 |
==表== CSS选择器描述
但实际上小程序所支持的CSS种类远超上表所列出的,笔者在这几年的小程序开发过程中几乎没有遇到不能够在小程序中使用的CSS选择器。也就是说,小程序所支持的CSS选择器种类远远超过上表所列举的6种。比如子元素选择器 > 就是可以在使用的。
CSS1、CSS2和CSS3的选择器种类加起来总计几十种,笔者无法保证全部的CSS选择器都可以在小程序中使用,但希望开发者不要拘泥于上表所描述的6种。
# 4.5 wxss中使用图片的注意事项
在wxss文件中使用图片需要土鳖注意:本地资源是无法在wxss中使用的。比如background-image,如果使用的是本地图片,则无法显示。但网络图片可以在wxss中使用。
如果我们的确需要在wxss中加载本地资源,那么我们可以将本地资源比如图片转换成base64格式。wxss是可以加载本地的base64资源的。
# 4.6 Flex布局
在上个小节中,welcome.wxss文件中的.container样式使用了一个display:flex的样式。那么,什么是Flex?
Flex布局是W3C组织在2009年提出的一个新的布局方案,其宗旨是让页面的样式布局更加简单,并且可以很好地支持响应式布局。这并不是小程序所独有的技术,它本身是CSS语法的一部分。只不过早期时候,主流的浏览器对Flex布局的支持并不完善,造成了很多开发者不知道有这种布局的存在或者使用非常少,我们还是习惯使用传统的position和float属性来布局。但传统的布局方式有它的缺陷,比如像垂直居中就不是那么容易实现,Flex可以很好地解决这些问题。
小程序能够非常好地支持Flex布局,并且这也是官方推荐的布局方式,我们结合==代码清单(4-3 小节中编写welcome页面的样式)== 来学习一下Flex布局的基础知识。
Flex也称为“弹性布局”,主要作用在容器上,比如 ==代码清单 3-7== 样式名为.container的view组件,就是一个容器,它将页面中所有的元素都包裹起来。
我们使用display:flex将这个view变成了一个弹性盒子。设置display:flex是应用一切弹性布局属性的先决条件,如果不设置display:flex,那么后续的其他相关弹性布局属性都将无效。
接着我们使用flex-direction这个属性指定view内元素的排列方向。这个属性可能的值有4个:
- row
- column
- row-reverse
- column-reverse
要理解这4个属性,首先要了解一个Flex布局非常重要的概念:轴。 我们知道,在一个平面直角坐标系里,轴有两个方向(X、Y),分别是水平方向和垂直方向。一个弹性盒子,需要确定一个主轴。这个主轴到底是水平方向还是垂直方向,就由flex-direction这个属性的值来确定。如果flex-direction值为row或者row-reverse,那么主轴的方向为水平方向,相反,如果值为column或者column-reverse,那么主轴为垂直方向。选定主轴的方向后,另外一个方向的轴我们成为“交叉轴”。也就是说,主轴并不一定就是水平方向,交叉轴也并不一定就是垂直方向,主轴的方向由flex-direction的取值来决定。理解这一点尤其重要。我们来看看 ==图3-8~图3-11== 所示的几张图。
图:flex-direction:row 时的主轴方向
图:flex-direction:row-reverse 时主轴的方向及子元素排列
图:flex-direction:column 时主轴的方向及子元素排列
图:flex-direction:column-reverse时主轴的方向及子元素排列
==图3-8到图3-11== 显示了当flex-direction取不同值时,主轴方向及子元素排布的情况。注意观察每张图里3个小item的排布顺序,主轴方向不同,子元素排布的方向也不同。
- flex-direction:row 时,主轴水平,方向为自左向右
- flex-direction:row-reverse时,主轴水平,但方向为自右向左
- flex-direction:column时,主轴垂直,方向自上而下
- flex-direction:column-reverse时,主轴垂直,方向自下而上
讲道理不如直接看效果。我们修改一下 ==代码清单3-7== 来看下4种属性的效果。将代码清单3-7里.container样式中的flex-direction的值分别替换为row、column、row-reverse、column-reverse。为了排除其他属性的干扰,我们将.container样式中的align-items:center也先注释掉。注释代码的快捷键为Ctrl+/。得到的效果 ==如图3-12~图3-15== 所示。
图:flex-direction值为row时三元素自左向右
图3-13:flex-direction值为row-reverse时三元素自右向左
图3-14:flex-direction值为column时三元素自上而下
图3-15:flex-direction值为column-reverse时三元素自下而上
是不是非常清楚?至于row和row-reverse这两张图中,“Hello,桔子罐头”往上偏移了一些,是因为受到welcome.wxss样式表中其他CSS属性的影响。但这3个元素的主要排布方向特征正如我们预期的一样。
根据设计图的样式,我们应该选择flex-direction:column作为我们的主轴。效果就如 ==图3-14== 所显示的那样。
虽然welcome页面的3个元素已经呈现出了垂直排列,但他们还没有居中显示。.container样式中的属性align-items: center,可以让三元素水平居中。 align-items属性定义子元素在交叉轴上如何对齐。这里特别要注意,align-items定义的是“交叉轴”这个方向上子元素的对齐方式。比如,由于我们在.container样式中设置了垂直方向为主轴,那么交叉轴就是水平方向,所以align-items:center将设置三元素在水平方向上的对齐方式为居中。
当然,align-items属性值不是只有center这一种,还有其他若干种取值。本书主要讲解小程序相关知识,限于篇幅这里就不再展开赘述这些CSS的相关知识。由于Flex布局在小程序里地位相当之高,本小节权当抛砖引玉,各位读者可以自行查找资料,更详细深入的学习Flex布局。
这里推荐一个学习方法。编程里的知识点是非常细小而琐碎的,学习不同的知识应该掌握不同的方法。对于学习CSS这类知识,笔者认为较好的学习方法应该是在实践中学习,而不是像我们上学时那样先把理论知识认真的学习一遍,甚至要求全部记住,这一点是不可取的。比如Flex布局的学习,我们首先应当大致浏览一下整个Flex的知识树,知道Flex解决了什么问题,有什么特点,大致有几类属性就够了。当我们在做项目遇到布局问题时,脑海里就能意识到Flex可能可以解决这个问题。接着我们抱着试试看的心态,带着目的去查找Flex布局的相关资料,即解决了问题,又能在实践中加深对Flex布局的理解,这比单纯死记硬背效果要好很多。人脑总是对形象化的东西记忆特别深刻,所以我们应当尽量在实践中学习知识。当然,也有可能Flex不能解决问题,但你查找和尝试解决问题的这个过程本身就是很好的学习手段。
# 4.7 小程序自适应单位rpx简介
不知道大家是否注意到,在welcome.wxss样式表中,我们绝大多数的长度单位都设置的是rpx这个全新的单位,比如margin-top:100rpx。在小程序里,长度单位既可以使用rpx,也可以使用px。使用rpx可以使组件自适应屏幕的高度和宽度,但使用px不会。要透彻地理解rpx需要对移动端分辨率有一定的了解,比如物理分辨率px、逻辑分辨率pt等概念。这里只需要记住以下的结论,如果你不是很明白本章的内容,也没有关系,只需要记住结论即可,并不影响我们的开发。 建议以iPhone 6的宽度750个物理像素作为标准,来做设计图。在此宽度下,这张设计图里每个元素的尺寸转换到小程序样式时,转换比例为1物理像素 = 1rpx =0.5px。rpx和px就是小程序样式里可以使用的两种长度单位。 举个例子,我们的welcome页面设计图的宽度总长是750像素,它是以iPhone 6的尺寸来设计的,而其中的头像图片高宽为200像素×200像素。如果想在iPhone 6里正确地显示这张200像素×200像素的图片,那么相应地image组件的高宽应该设置为多少呢?
答案是要不就设置为高200rpx,宽200rpx,要不就设置为高100px,宽100px。这两个单位,在iPhone 6下显示效果一样,但如果我们将模拟器切换到其他机型,这两种不同的单位就会出现差异。rpx将随着屏幕尺寸的变化而变化,但px不会。那么到底选择rpx还是选择px呢?这取决于你需要元素随着移动设备尺寸的变化而变化,还是让元素始终保持不变,需要具体问题具体分析。
对于margin-top或者是image组件的高宽,很多时候,需要他们随着设备的尺寸不同动态地变化,以保持页面元素之间的分布可以保持“一定的比例关系”,这种情况下应该使用rpx。来看下面这个例子。
请开发者不断的切换模拟器的不同机型(iPhone 5、iPhone 6、iPhone 7 Plus等),我们会发现虽然机型不同,但页面的3个元素之间、元素与页边距之间的比例还是非常和谐和美观的,整体上页面的布局没有不成比例。也就是说由于rpx的作用,每个元素的大小会随着机型的不同自适应变化尺寸或者间距以保持这个比例关系。
接着我们将image组件的样式.avatar更改为以下代码:
==代码清单3-8 将image组件的高宽单位由rpx更改为px==
.avatar{
width:200px;
height:200px;
margin-top:160rpx;
}
2
3
4
5
将image组件的高宽单位由rpx更改为px后,我们再重复进行不同机型的切换,会发现avatar图像并不会随着机型的不同而做出”自适应“的缩放,比例变的不再那么协调了。这是因为px单位是不会根据不同机型的屏幕尺寸做自适应调整的。
开发者们可以自己将welcome.wxss里的rpx和px相互替换一下,或者多调整一下模拟器的机型,来感受一下rpx和px的不同。
那是不是rpx就是万能的,我们可以将页面里的所有元素的长度单位都换成rpx呢?来看看下面这个例子,在welcome.wxss里有一段这样的代码:
==代码清单3-9 welcome.wxss== .journey-container{ margin-top: 200rpx; border: 1px solid #405f80; width: 200rpx; height: 80rpx; border-radius: 5px; text-align:center; }
journey-container设置了“开启小程序之旅”这段文本的外边框。为什么其他的元素我们都使用rpx为单位而唯独border这个属性使用的是1px呢?。因为我们讲过,rpx是自适应单位,它通常非常适合用来控制图片的高宽和元素之间的间距(通常这些元素需要随着屏幕尺寸的不同而动态变化)。但我们考虑一下,border这个属性需要动态变化吗?不需要。如果border动态变化,那么它会在屏幕尺寸较大的手机上变得很粗,这并不是我们想要的效果。所以这里应当将border的单位设置为px。同理,使用px作为border-radius的单位。
最后,我们为什么要强调最好是在iPhone 6的尺寸下做设计图呢?因为只有在iPhone 6的尺寸下,设计图里的1个像素才满足下面的转换关系:
1物理像素 = 1rpx = 0.5px
如果不以iPhone 6的标准来做设计图,也是可以的。但非iPhone 6的尺寸下,设计图与rpx、px的转换关系就不是整数倍的,计算起来比较麻烦,所以建议设计图最好以iPhone 6的尺寸标准来设计,这样换算起来很方便。这也是官方建议的一个设计标准。
如果我们足够细心,可以看到小程序的模拟器选择项下,给出了每种机型的分辨率。要强调的是,这里的分辨率指的是逻辑分辨率pt,而非物理分辨率。以iPhone 6为例,模拟器里给出的分辨率是:375×667、Dpr:2 它的意思是:iPhone 6的水平方向有375个逻辑像素点,而竖直方向有667个逻辑像素点,每个逻辑像素点包含2个物理像素点。开发者一定要注意逻辑像素和物理像素的区别。我们通常在PS里做的设计图,它的像素可以简单理解为物理像素。 再次提醒开发者,1物理像素不等于1px。假设有一张图片在操作系统下显示宽度为750个像素,我们现在想让这个图片水平方向充满整个页面。如果你直接在页面里(iPhone 6模拟机型下)将图片宽度设置为750px,这是不对的。正确的设置方法为750rpx或者375px,才能让图片水平填满小程序。因为,iPhone 6下: 1物理像素=1rpx=0.5px。
# 4.8 全局样式文件app.wxss
到目前为止,你可能不太喜欢welcome欢迎页面中所使用的字体。默认情况下,小程序将使用系统的默认字体,但你可能想更改为任意你想设置的字体。
最简单的更改字体的方法是在welcome.wxss中加入如下代码:
==代码清单3-10 改变页面text组件字体== welcome.wxss
text{
font-family: "PingFangSC-Thin";
}
2
3
代码会将welcome页面中的所有text组件的字体更改为苹果的苹方细体。
这里我们需要说明一下,苹方细体只有在苹果的设备上才被支持。如果你使用的是Windows操作系统,你可以换用任意Windows所支持的字体。 同时,在设置完上面的代码后,小程序所显示的字体可能并没有变化。这是因为在welcome.wxss中,有几处代码里我们设置了font-weight: bold,加粗的字体会和苹方细体相冲突,导致我们看不出来字体的改变。请开发者暂时注释掉font-weight:bold的代码段,即可预览到字体的变化。
图:默认的字体UI
图:苹方细体UI
那我们思考一个问题,假如现在有100个页面,而100个页面里几乎所有的字体都需要使用PingFang字体。在100个页面的wxss中重复设置字体这并不是一个很好的解决方案。
所以,我们需要有一个全局样式表,可以为所有页面设置“默认”样式。小程序为我们提供了一个这样的样式表文件,就是前面提到过的app.wxss文件。
我们将==代码清单3-10==的代码,复制到项目根目录下的app.wxss文件中。虽然这段代码不在welcome.wxss页面样式表中,但依然可以使welcome页面的text组件字体更改为PingFang字体。还可以在这里设置一些其他的公共样式,比如字体大小font-size、字体颜色color等。
如果不想在某个页面中使用全局默认样式,那么只需要在相应 页面 的wxss文件中重新定义这个样式即可。小程序会优先选择页面wxss中的样式,而不是app.wxss中的样式。也就是说对于相同的样式,页面的wxss将会覆盖app.wxss中的样式。
# 4.9 小程序字体设置与动态加载字体
小程序官方文档中目前没有明确指明字体设置的一些规则。所以,我们在这里做一些补充说明。
小程序默认情况下将使用操作系统的默认字体,比如Windows下的微软雅黑。Mac下的默认字体比较多变,不同的Mac OS版本的默认字体也有所不同。
考虑到小程序目前总共有3个运行环境:本地操作系统(开发时)、iOS真机、Android真机,如果我们不指定任何字体,那么小程序将显示这些环境中的默认字体。如果我们要指定小程序的字体,那么需要当前环境支持这种字体,才能被显示出来。所以,我们开发者在设置字体时,可以设置多种字体,而不要仅仅只设置一种字体。当然,大多数情况下,对于小程序来说,系统的默认字体就足够了。只有当我们需要去做一些个性化的小程序时,才会去设置一些特别的字体。
==代码清单 设置多种字体示例==
text{
font-family:'Times New Roman', Times, serif;
}
2
3
有时候,我们的小程序非常的个性,需要的字体可能在iOS和Android上都不支持,那么我们可以使用wx.loadFontFace这个方法动态的加载字体并设置字体。以下是官方文档中对此API的描述,我们摘取出来以供开发参考。如果你对字体没有什么需求,以下内容无需了解,确实用的不多。
wx.loadFontFace 可以动态加载网络字体。文件地址需为下载类型。iOS 仅支持 https 格式文件地址。使用动态加载字体接口的开发者还需要注意以下几点:
- 引入中文字体,体积过大时会发生错误,建议抽离出部分中文,减少体积,或者用图片替代
- 字体链接必须是https(ios不支持http)
- 字体链接必须是同源下的,或开启了cors支持,小程序的域名是servicewechat.com
- canvas等原生组件不支持使用接口添加的字体
- 建议格式为 TTF 和 WOFF,WOFF2 在低版本的iOS上会不兼容
- 工具里提示 Faild to load font可以忽略
- 建议格式为 TTF 和 WOFF,WOFF2 在低版本的iOS上会不兼容
# 4.10 页面的根元素page
到目前为止,我们的welcome页面已经像那么回事儿了。但页面的样式和设计图还不太一样,设计图中整个页面的底色呈现的是橘红色,而现在的页面还是白色。那么,来修改一下页面的背景颜色吧。
要修改页面整体的背景色,需要寻找一个包裹所有页面元素的容器,并设置这个容器的背景色。那么,首先尝试给页面最外层view(class = "container" 的这个view)一个背景色。在welcome.wxss文件中的.container样式里新增属性background-color: #ECC0A8。
==代码清单3-11 向.container中新增背景色 welcome.wxss==
.container{
display: flex;
flex-direction:column;
background-color:#ECC0A8;
}
2
3
4
5
4 New
接着保存预览一下增加样式后的页面,它将呈现==如图3-18==所示的效果。
图:设置容器view背景色后的页面效果
我们可以看到,并不是整个页面都呈现出橘红色,只是有元素占据的地方才呈现出橘红色。是不是应该给这个View加上一个height: 100%;
的高度呢?我们可以尝试加上,但这依然没有效果。
这个原因在于最外层的View的高度由其内部子元素决定,所以橘红色部分的下边刚好和按钮的下边重合。如何解决这个问题呢?
是否可以通过给View一个固定的高度来解决这个问题?固定的高度在某些机型上是可以的,但这并不是最好的办法。因为用户的机型是不确定的,所以屏幕的尺寸也是不一样的,固定的高度无法去适配不同的机型。设置固定的高度可能会使页面出现滚动条,也可能橘红色依然无法覆盖整个页面。
当然,用我们前面学到的rpx是可以解决这个问题的,将View的高度单位设置为rpx,就可以让它随着不同的机型进行自适应调整。但这看起来很蠢,高度具体设置多少,还需要我们了解iPhone 6的屏幕分辨率。所以,这依然不是一个很好的解决方案。
其实,在View的外边,小程序还有一个默认的容器元素:page。我们可以在开发工具中的Wxml这个Pannel中看一下welcome页面的页面结构,==如图3-19==所示。
图:用WXML工具查看页面架构
在class= “container” 这个view的外边还有一个容器元素:page。这是MINA框架为小程序默认添加的。每个小程序页面的最外层都有这个page元素。page元素代表着页面这个整体,所以只需要对page设置背景色即可实现设计图里的效果。在welcome.wxss文件的尾部追加以下样式:
==代码清单3-12 对page设置背景颜色 == welcome.wxss
page{
background-color:#ECC0A8;
}
2
3
保存后,页面将呈现出==如图3-20==所示的效果:
图:对page设置背景色后的页面效果
page代表着整个页面的容器,如果想对页面整体做样式或者属性设置,那么应该考虑page这个页面的根元素。 现在,welcome页面的顶部还有一块儿黑黢黢的长条,这实在是太难看了。我们将在后续的小节中解决这个问题。
# 4.11 app.json中的window配置项
还记不记得在之前小节中,我们使用了app.json的一个配置项pages,用来注册小程序页面文件?这一小节,我们来学习app.json的另外一个配置项window。
window配置可项用来设置小程序的状态栏、导航栏、标题和窗口等部分的特征。它的配置项非常的多,下面列举出window下面的一些常用子项:
- navigationBarTextStyle 配置导航栏文字颜色,只支持 black/white。
- navigationBarTitleText 配置导航栏文字内容。
- navigationStyle 自定义导航栏 可选 default/custom
- backgroundColor 配置窗口颜色。
- backgroundTextStyle 下拉背景字体,仅支持dark/light。
- enablePullDownRefresh 是否开启下拉刷新。
正如本书前面所讲,把官方文档中全部window子项(早期版本只有6个子项,新版本已有16个子项)的详细说明全部列举在书中并没有意义,只会浪费本书的篇幅,并被贴上一个“抄文档”的标签。当我看书时,我也及其反感大量摘抄官方文档的做法。书中的内容应该是作者的感悟与经验,而不应该是官方文档的纸质版。
==本书采取的做法是,将这些配置子项尽可能多地编排和融合在Orange Can项目中,在案例实践中演示这些组件、属性、以及配置项的的具体作用。对于官方文档讲的不清楚、不明白或者错误的地方,本书也会做出补充和修正。如果想详细了解每个配置项的意义,可异步到官方文档中查看,文档中的API说明永远是最新的(但我并不建议花时间去看这些API的详细说明,因为看了也白看,看完即忘是很正常的)。
但实际上,我甚至不建议大家在这些配置上的意义上花时间去浏览官方文档。对于每个小程序的知识点,我们在学习阶段最需要关心的只有两点——有什么用和怎么用。比如window这个配置项,我们只需要关心以下两点即可:
- window是做什么的?
- 怎么使用window这个配置项?
至于window下面的属性值,建议具体问题具体对待,不需要现在就搞明白。把这些属性值放到实际的工作项目中学习,不仅节约时间而且印象更加深刻。比如,下个小节我们将解决目前welcome页面顶部导航栏的问题,我们就可以查阅官方文档,并在window配置项中寻找能够解决这个问题的方案。
Orange Can项目的后期代码里,我们还会使用到很多window的其他配置项,让我们继续项目,逐步学习。
书籍绝大多数是用来引导入门和分享思想的,它不应该替代官方的API文档。API文档不同于“tutorial”或者“get started”(优秀的开发文档都有这两个部分),它一般用于查阅,是工具而非教程,通常都非常简洁。这里分享一个学习API的方法,就是“试”。 对于window这个配置项,你只需要将其他几个属性加入到window中,再更改几个可能的属性值,就可以立刻即时地预览到属性值的效果。如果属性值的效果不符合你的预期,就具体去分析为什么会出现这种情况。不知不觉中,你对整个API就会越来越熟悉。 当然,还是我们之前提到过的,不需要把整个API文档都试一遍,当你需要解决问题时,结合具体的案例再来“试”这些API。
# 4.12 取消默认顶部导航栏
==图3-20==的顶部黑色长条是小程序默认的导航栏。我们需要更改这个导航栏的颜色,让整个welcome页面都呈现出统一的橘红色。有两种方法可以实现这个目标:
- 把导航栏的颜色由默认的黑色更改为橘红色
- 直接去掉导航栏
在小程序早期的版本中,顶部导航栏是无法取消的,所以本书的第一版选择的方式是方案1。这个方案也是非常容易想到的——改变颜色即可。但在新版本中,我们可以采用方案2,因为新版本的小程序允许我们取消这个导航栏。
通过查阅官方文档,我们能很轻易的找到一个名为navigationStyle
的window配置项。配置这个选项,可以取消掉小程序默认的导航栏。
==代码清单 取消顶部导航栏== app.json
{
"pages": [
"pages/welcome/welcome"
],
"window": {
"navigationStyle":"custom"
}
}
2
3
4
5
6
7
8
7 New
navigationStyle
有两个取值:
- default 默认样式
- custom 自定义导航栏,只保留右上角胶囊按钮
当我们把navigationStyle
配置成custom后,顶部黑色区域的导航栏消失,整个页面都呈现出橘红色来。但我们会注意到,小程序右上角默认的”胶囊“按钮并不会取消掉。很多开发者都会询问是否有方法可以取消掉小程序默认的胶囊按钮?目前版本是不可以的。
取消导航栏这个配置项对于小程序的UI设计来说,非常重要。早期的小程序由于强制保留这个导航栏,导致小程序无法设计出更加个性化的UI,比如==下图==这种在APP上很常见的浸入式UI就无法实现。注意看下图的顶完全交给开发者自由定制,并无系统导航栏强制占据顶部。
图:Luckin Coffee App,页面顶部的浸入式设
但通过配置”custom“,就可以在小程序中实现这种个性化的UI风格。
> 如果我们想使用方案1,通过更改导航栏颜色来达到目的应该怎么做呢?还是查阅文档,我们会发现navigationBarBackgroundColor
这个配置项是可以修改导航栏颜色的。可以通过配置这个选项更改导航栏颜色来达到和方案2同样的效果。
但这里要注意的是,如果我们取消了导航栏,那么页面整体的元素将像上偏移一定的高度,因为导航栏没有了。如果我们想要还原原来的高度,可以增加welcome.wxss中avatar这个css的margin-top。原来我们设置的是160rpx,现在,我们可以增加导航栏的高度100rpx,160+100 = 260rpx。最终,avatar的margin-top需要设置成260rpx。
# 4.13 页面的配置文件
上个小节,我们通过设置app.json中window的navigationStyle
,将welcome页面的顶部导航栏取消了。之前我们曾聊到过,app.json是全局配置文件,在这个配置文件中所配置的属性,大多数情况下都是全局的。这也意味着,不仅是welcome页面中的导航栏将被取消,以后我们新建的所有页面的顶部导航栏都将被取消掉。
这看起来不太合理,因为我们可能只想让welcome这个首屏页面是没有导航栏的“全屏”模式,其他的页面我们依然需要系统的导航栏。那如何解决这个冲突?很显然,我们不应当在app.json中配置navigationStyle
,而应当在welcome.json中配置这个选项,因为页面的json文件只会对当前页面起作用,并不会影响其他页面。下面我们来试一试,在welcome.json中添加以下代码:
==代码清单 添加配置项== welcome.json
{
"usingComponents": {},
"navigationStyle": "custom"
}
2
3
4
3 New
usingComponents
这个配置项是小程序工具为每个页面默认生成的,它用于引用自定义组件,我们将在后续章节中深入讲解小程序的自定义组件。这里,我暂时先忽略这个配置项。
可以看到,我们仅仅只在welcome.json中增加了一段navigationStyle
的配置,它其实同之前我们在app.json的window中配置的选项一模一样。为了测试的准确,请开发者将app.json中的navigationStyle
给删除掉。保存后,我们会发现在welcome.json中配置navigationStyle
依然是有效果的,它同样可以取消掉系统的导航栏。但这里我们要强调,在welcome.json中配置的navigationStyle
只对welcome页面有效果,并不会影响其他页面。
事实上,绝大多数在app.json的window中可以配置的属性,都可以在页面的.json文件中进行配置。同时,页面的.json文件只能配置app.json的window下的属性,比如pages这个配置项就不能够在welcome.json中配置。
我们还需要注意,页面的json文件是不需要写window这个名字的,直接将navigationStyle
写在json的{ }中就可以了。以下的写法是不对的:
==代码片段 错误的welcome.json配置形式 == welcome.json
{
"usingComponents": {},
"window": {
"navigationStyle": "custom"
}
}
2
3
4
5
6
记住,页面的json文件只能配置window相关属性,所以就不需要再很啰嗦的加上window这个名字了。
- 上一页
- 首页
- 1
- 尾页
- 下一页
- 总共1页